//
// Created by forec on 17-5-7.
//

#include "../include/communication.h"

int set_nonblocking(int fd)
{
    int flags;
    if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
        flags = 0;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}


int listenOn(uint16_t port) {
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htons(INADDR_ANY);
    addr.sin_port = htons(port);
    int _socket = socket(PF_INET, SOCK_STREAM, 0);
    if (_socket < 0)
        return _socket;
    int keepAlive = 1;
    int keepIdle = 3;
    int keepInterval = 2;
    int keepCount = 3;
    if (setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE, (void *) &keepAlive, sizeof(keepAlive)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep alive: %d\n", errno);
    if (setsockopt(_socket, SOL_TCP, TCP_KEEPIDLE, (void *) &keepIdle, sizeof(keepIdle)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep Idle: %d\n", errno);
    if (setsockopt(_socket, SOL_TCP, TCP_KEEPINTVL, (void *) &keepInterval, sizeof(keepInterval)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep Interval: %d\n", errno);
    if (setsockopt(_socket, SOL_TCP, TCP_KEEPCNT, (void *) &keepCount, sizeof(keepCount)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep CNT: %d\n", errno);
    if (bind(_socket, (struct sockaddr *)&addr, sizeof(addr)))
        return -1;
    if (listen(_socket, GROUP_SIZE))
        return -1;
    return _socket;
}

int acceptAt(int sock) {
    struct sockaddr_in remoteAddr;
    socklen_t length = sizeof(remoteAddr);
    int keepAlive = 1;
    int keepIdle = 3;
    int keepInterval = 2;
    int keepCount = 3;
    int nsock = accept(sock, (struct sockaddr *)&remoteAddr, &length);
    if (setsockopt(nsock, SOL_SOCKET, SO_KEEPALIVE, (void *) &keepAlive, sizeof(keepAlive)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep alive: %d\n", errno);
    if (setsockopt(nsock, SOL_TCP, TCP_KEEPIDLE, (void *) &keepIdle, sizeof(keepIdle)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep Idle: %d\n", errno);
    if (setsockopt(nsock, SOL_TCP, TCP_KEEPINTVL, (void *) &keepInterval, sizeof(keepInterval)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep Interval: %d\n", errno);
    if (setsockopt(nsock, SOL_TCP, TCP_KEEPCNT, (void *) &keepCount, sizeof(keepCount)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep CNT: %d\n", errno);
    //set_nonblocking(nsock);
    return nsock;
}

int dialTo(const std::string & remoteIP, uint16_t port) {
    int localSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (localSocket < 0)
        return localSocket;
    struct sockaddr_in remoteAddr;
    bzero(&remoteAddr, sizeof(remoteAddr));
    remoteAddr.sin_family = AF_INET;
    if (inet_aton(remoteIP.c_str(), &remoteAddr.sin_addr) == 0)
        return -1;
    remoteAddr.sin_port = htons(port);
    socklen_t remoteAddrLength = sizeof(remoteAddr);
    if (connect(localSocket, (struct sockaddr*) & remoteAddr, remoteAddrLength) < 0)
        return -1;
    int keepAlive = 1;
    int keepIdle = 3;
    int keepInterval = 2;
    int keepCount = 3;
    if (setsockopt(localSocket, SOL_SOCKET, SO_KEEPALIVE, (void *) &keepAlive, sizeof(keepAlive)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep alive: %d\n", errno);
    if (setsockopt(localSocket, SOL_TCP, TCP_KEEPIDLE, (void *) &keepIdle, sizeof(keepIdle)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep Idle: %d\n", errno);
    if (setsockopt(localSocket, SOL_TCP, TCP_KEEPINTVL, (void *) &keepInterval, sizeof(keepInterval)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep Interval: %d\n", errno);
    if (setsockopt(localSocket, SOL_TCP, TCP_KEEPCNT, (void *) &keepCount, sizeof(keepCount)) == SOCKET_ERROR)
        fprintf(stderr, "Cannot set socket keep CNT: %d\n", errno);
    //set_nonblocking(localSocket);
    return localSocket;
}

long sendData(int sock, char * buf, size_t len) {
    ssize_t sent = 0;
    while (len > 0) {
        ssize_t realWriteCount = write(sock, buf + sent, len);
        if (realWriteCount < 0) {
            if (errno != EINTR) {
                fprintf(stderr, "Connection error! Send to sock %d with %ld write count!\n", sock, realWriteCount);
                return realWriteCount;
            } else {
                fprintf(stderr, "Connection error! Get EINTR in sock %d, retry in 1s!\n", sock);
                std::this_thread::sleep_for(std::chrono::seconds(1));
                continue;
            }
        }
        len -= realWriteCount;
        sent += realWriteCount;
    }
    return sent;
}

ssize_t readUntil(int sock, char * buf, size_t len) {
    ssize_t readed = (ssize_t )len;
    while (readed > 0) {
        ssize_t cur = read(sock, buf, (size_t)readed);
        if (cur < 0) {
            if (errno != EINTR) {
                fprintf(stdout, "Connection error! Cannot read from sock %d!\n", sock);
                return cur;
            } else {
                fprintf(stdout, "Connection error! Get EINTR in sock %d, retry in 1s!\n", sock);
                std::this_thread::sleep_for(std::chrono::seconds(1));
                continue;
            }
        }
        readed -= cur;
    }
    return readed;
}


void destroyConnection(int sock) {
    close(sock);
}
